<?php
/**
 * GeryEngine
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this release in the file LICENSE
 *
 * @copyright  Copyright (c) 2007 Arjen Brouwer
 * @license    New BSD License
 * @author     Arjen Brouwer
 */

class GeryCompileEngine {
	
	private static $classesLoaded = false;
	
	private $input;
	
	/**
	 * The token parser
	 *
	 * @var Parser
	 */
	private $parser;
	
	/**
	 * The lexer
	 *
	 * @var Lexer
	 */
	private $lexer;
	
	private $logger;
	
	private $directives;
	
	function __construct($logger) {
		
		$this->logger = $logger;
		
		$logger->info("Initializing GeryCompileEngine");
		
		// load helper classes
		self::loadClasses();
		
		$logger->info("GeryCompileEngine: classes loaded");
		
		// create an empty macro array
		$this->macros = array();
		
		// create the parser and lexer
		$this->parser = new Parser();
		$this->parser->setCompileEngine($this);
		
		$this->lexer = new Lexer('');
		$this->lexer->setCompileEngine($this);
		
		$logger->info("GeryCompileEngine: initialization complete.");
	}
	
	public function compile(Resource $resource) {
		
		// add smurk to the end for the lexer
		$this->input = $resource->getData();
		
		$this->logger->debug("GeryCompileEngine: feeding template '" . $resource->getName() . "' to lexer (" . strlen($this->input) . " bytes)");
		
		// initialize lexer
		$this->lexer->reset();
		$this->lexer->setInput($this->input);
		
		$this->logger->debug("GeryCompileEngine: start lexing and parsing");
		
		// start lexing and parsing
		while (($token = $this->lexer->yylex()) !== false) {

			// ignore null tokens
			if ($token === null) 
				continue;
			
			// echo $this->parser->getSymbolName($token->type) . ": " . $token->value . "<br />";
			
			// pass token to the parser
			$this->parser->parse($token->type, $token);	
		}
		
		// finalize the parser by sending a null token and calling the 
		// finalize() method that pops all the left over rules from the stack
		$this->parser->parse(0, null);
		$this->parser->finalize();
		
		$this->logger->debug("GeryCompileEngine: parsing done");
		
		// create the code generator
		$generator = new TemplateCodeGenerator($resource);
		
		// fetch the abstract syntax tree
		$ast = $this->parser->getAST();
		
		// and let the the root node of the AST accept the visitor, if there
		// is an valid AST. The AST is null if there was no input.
		if ($ast) {
			
			// let the AST accept a new TreeVisitor
			$ast->accept(new TreeVisitor($this, $generator));	
		}
		
		// return the compiled template
		$compiledTemplate = $generator->output();
		
		// generate a unique identifier for the template
		$className = uniqid('__template__');
		
		$compiledTemplate = str_replace('__TEMPLATEID__', $className, $compiledTemplate);
		
		return new CompiledTemplate($className, $compiledTemplate);
	}
	
	public function getInputSubstring($start, $length) {
		return substr($this->input, $start, $length);	
	}
	
	/**
	 * Get the line number of the lexer
	 *
	 * @return int Line number
	 */
	public function getLine() {
		// add 1 to the line numbers because it starts counting zero
		return $this->lexer->yyline + 1;
	}
	
	/**
	 * Get the input lexer
	 *
	 * @return Lexer
	 */
	public function getLexer() {
		return $this->lexer;
	}
	
	/**
	 * Get the parser
	 *
	 * @return Parser
	 */
	public function getParser() {
		return $this->parser;
	}
	
	private static function loadClasses() {
		
		// check if classes were already loaded
		if (self::$classesLoaded)
			return;
			
		// load classes
		$basePath = dirname(__FILE__);
		
		require_once($basePath . '/lib/compiler/generator/ICodeGenerator.php');
		require_once($basePath . '/lib/compiler/generator/CodeGeneratorOptions.php');
		require_once($basePath . '/lib/compiler/generator/CodeGenerator.php');
		require_once($basePath . '/lib/compiler/generator/ProxyCodeGenerator.php');
		require_once($basePath . '/lib/compiler/generator/BufferedCodeGenerator.php');
		
		require_once($basePath . '/lib/compiler/lexer/Lexer.php');
		require_once($basePath . '/lib/compiler/lexer/Token.php');
		
		require_once($basePath . '/lib/compiler/parser/Parser.php');
		require_once($basePath . '/lib/compiler/parser/ParserTokens.php');
		require_once($basePath . '/lib/compiler/parser/AST.php');

		require_once($basePath . '/lib/compiler/TreeVisitor.php');
		require_once($basePath . '/lib/compiler/TemplateCodeGenerator.php');

		self::$classesLoaded = true;
	}
}
?>